//----------------------------------------------------------------------------
// M A R I T I M E  R E S E A R C H  I N S T I T U T E  N E T H E R L A N D S
//----------------------------------------------------------------------------
// Copyright (C) 2009 - MARIN - All rights reserved - http://www.marin.nl
//----------------------------------------------------------------------------
// Program    : various
// Module     : various
// File       : OsgViewer.h
//----------------------------------------------------------------------------

#ifndef OSGVIEWER_H
#define	OSGVIEWER_H

#include <osgViewer/Viewer>
#include "Array2D.h"

// forward declarations
namespace osg
{
    class ShapeDrawable;
    class Vec3d;
}


// TODO put this function in some general library
void microsleep(long microseconds);


namespace osgviewer
{

// forward declarations
class TextBox;
class ScreenCaptureHandler;

// convenience typedefs to easily define callbacks
typedef bool (*KeyHandler)(int);
typedef bool (*ClickHandler)(const osg::Vec3&, const std::string& name, bool ctrl);
typedef void (*AnimationHandler)();

// default second position parameter
const osg::Vec3 defpos(1e30, 1e30, 1e30);

//----------------------------------------------------------------------------
// class OsgViewer
//----------------------------------------------------------------------------
/// general viewer for geometric shapes
/** enables programmers with very limited osg knowledge to make a
 *  visualization of geometric shapes plus an optional wave field */
//----------------------------------------------------------------------------
class OsgViewer : public osgViewer::Viewer
{
    typedef osgViewer::Viewer super;

    friend class MouseEventHandler;
    friend class KeyboardEventHandler;

public:

    OsgViewer(const std::string& caption);

    virtual ~OsgViewer();

    void setCaptionAttributes(const osg::Vec3& position, float size, const std::string& color);

    void setCaption(const std::string& caption);

    void setScale(char dir, float factor);

    void setViewingDistanceFactors(const osg::Vec3& xyz);

    void setEye(char dir);

    void setXyzHomeDistance(float distance);

    void getHome(osg::Vec3d* pEye, osg::Vec3d* pCenter, osg::Vec3d* pUp) const;

    void setHome(const osg::Vec3d& eye, const osg::Vec3d& center, const osg::Vec3d& up);

    void start();

    int run();

    bool plot();

    void quit();

    const osg::BoundingSphere& getBound(const std::string& name = "") const;

    void setKeyHandler(osgviewer::KeyHandler handler);

    void setLeftClickHandler(osgviewer::ClickHandler handler);

    void setRightClickHandler(osgviewer::ClickHandler handler);

    void setAnimationHandler(osgviewer::AnimationHandler handler);

    void addObject(const std::string &name, const std::string &type, const std::string &color,
                   float diameter, const osg::Vec3& xyz1, const osg::Vec3& xyz2 = defpos);

    void addPolygon(const std::string &name, const std::string &color,
                    const std::vector<osg::Vec3>& xyz);
    
    void addRectangle(const std::string &name, const std::string &color, float thickness,
                      const osg::Vec3 &xyz1, const osg::Vec3 &xyz2,
                      const osg::Vec3 &xyz3, const osg::Vec3 &xyz4);

    void addDynamicSurface(const std::string &name, const std::string& color,
                           const Array2D<float> *pField,
                           int xmin, int ymin, int xmax, int ymax, float dx, float dy);

    void addPanelRoot(const std::string &name);

    void addPanel(const std::string &rootname, const std::string &color,
                  const std::vector<osg::Vec3>& vertices,
                  const osg::Vec3& normal);

    void addText(const std::string &name, const std::string &color,
                 float size, const std::string& text, const osg::Vec3& xyz);

    void addAxes(const osg::Vec3& orientation, float length, float tick);

    void moveObject(const std::string &name, const osg::Vec3& xyz);

    void rotateObject(const std::string &name, const osg::Vec3& xyz);

    bool removeObject(const std::string &name);

    void removeAllObjects();

    void updateHeightFields();

    void toggleScreenCast();

    void toggleLightDirection();

    bool isScreenCasting() const;

    void writeOsgFile();

    void saveView();

    void loadView();

private:

    typedef std::vector<OsgViewer*> VIEWERLIST;       ///< list with all plot windows
    static VIEWERLIST m_ViewerList;                   ///< list with all plot windows

    typedef std::multimap<std::string, osg::Node*> ObjectList; ///< list with all drawable objects

    float                    m_xviewDistFactor;       ///< factor for viewing distance in x direction
    float                    m_yviewDistFactor;       ///< factor for viewing distance in y direction
    float                    m_zviewDistFactor;       ///< factor for viewing distance in z direction
    float                    m_zscale;                ///< exaggerated heightfield height factor
    osg::Group*              m_pRoot;                 ///< scenegraph root
    osg::Group*              m_pScene;                ///< to attach objects to
    osg::Camera*             m_pCamera;               ///< camera to handle viewpoint
    osg::LightSource*        m_pLight;                ///< (only) light in scene
    TextBox*                 m_pTextBox;              ///< displays bottom left text line
    osg::HeightField*        m_pHeightField;          ///< heightfield data TODO put in drawlist
    osg::ShapeDrawable*      m_pHeightFieldDrawable;  ///< heightfield visualization TODO put in drawlist
    const Array2D<float>*    m_pHeightFieldArray;     ///< heightfield source data TODO put in drawlist
    int                      m_xmin;                  ///< lower left of heightfield
    int                      m_ymin;                  ///< lower left of heightfield
    int                      m_xmax;                  ///< right upper of heightfield
    int                      m_ymax;                  ///< right upper of heightfield
    std::string              m_caption;               ///< bottom left caption
    KeyHandler               m_keyPressed;            ///< callback for key presses
    ClickHandler             m_clickedLeft;           ///< callback for clicking left
    ClickHandler             m_clickedRight;          ///< callback for clicking right
    AnimationHandler         m_animation;             ///< callback for animation
    osg::Vec3                m_eye;                   ///< original viewing position eye
    osg::Vec3                m_center;                ///< original viewing position center
    osg::Vec3                m_up;                    ///< original viewing position up
    float                    m_distance;              ///< default eye distance along x, y, z-axis
    ObjectList               m_Objects;               ///< list of all objects to draw
    float                    m_slopfactor;            ///< for clicking vs dragging
    bool                     m_capture;               ///< is a screencast running?
    ScreenCaptureHandler*    m_pScreenCaptureHandler; ///< handler for screencasting
    bool                     m_quit;                  ///< quit flag

    void setInitialHome(const osg::Vec3d& eye, const osg::Vec3d& center, const osg::Vec3d& up);

    void resetInitialHome();

    float getSlopFactor() const;

    /// @cond nevercall
    OsgViewer();
    OsgViewer(const OsgViewer& orig);
    OsgViewer & operator =(const OsgViewer& orig);
    /// @endcond

};

} // namespace osgviewer

#endif	/* OSGVIEWER_H */

